namespace NBody.KD
{
    using System;
    using NBody;
        
    public class LeafNode : Node
    {
        private int bucket_start;
        private int bucket_end;
        private IDParticle[] bucket;
           
        public LeafNode(int bucket_start, int bucket_end, IDParticle[] bucket, double[,] bounds)
        {
            this.bucket_start = bucket_start;
            this.bucket_end = bucket_end;
            this.bucket = bucket;
            
            this.n = bucket_end - bucket_start;
            this.mass = 0.0;
            this.cm = new double[] {0.0, 0.0, 0.0};
            for (int i = bucket_start; i < bucket_end; i++)
            {
                this.mass += bucket[i].Mass;
                for (int j = 0; j < 3; j++)
                    this.cm[j] +=  bucket[i].Mass * bucket[i].Position(j);
            }
            for (int j = 0; j < 3; j++)
                this.cm[j] /= this.mass;
                
            ComputeRmax(bounds);
        }
        
        public override void FindNearest(double rd, ref SearchInfo info)
        {
            for (int i = bucket_start; i < bucket_end; i++)
            {
                double dist2 = DistanceSqd(info.Position, bucket[i]);
                if (dist2 < info.PQ.TopPriority && dist2 != 0.0)
                {
                    info.PQ.Pop();
                    info.PQ.Push(i, dist2);
                }
            }
        }
        
        // Metric distance (squared).
        private static double DistanceSqd(double[] pos, Particle p2)
        {        
            return (pos[0] - p2.X) * (pos[0] - p2.X) +
                   (pos[1] - p2.Y) * (pos[1] - p2.Y) +
                   (pos[2] - p2.Z) * (pos[2] - p2.Z);
        }
        
        public override void CalcPotential(IDParticle p, double theta, double eps, ref double phi)
        {
            double eps2 = eps * eps;
            for (int i = bucket_start; i < bucket_end; i++)
            {
                if (bucket[i] != p)
                {
                    double x = p.X - bucket[i].X;
                    double y = p.Y - bucket[i].Y;
                    double z = p.Z - bucket[i].Z;
                    double r = Math.Sqrt(x * x + y * y + z * z + eps2);
                    phi -= bucket[i].Mass / r;
                }
            }
        }
    }
}
